package com.stormragetech.arthas.server;

import com.stormragetech.arthas.common.util.ArthasUtil;
import com.stormragetech.arthas.dispatcher.ArthasChannelInitializer;
import com.stormragetech.arthas.dispatcher.ArthasProcessHandler;
import com.stormragetech.arthas.spring.SpringUtil;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.SelfSignedCertificate;
import lombok.extern.log4j.Log4j2;
import org.springframework.context.ApplicationContext;

import java.net.InetAddress;

/**
 * description: server initialize method
 *
 * @author: Xue Bin
 * @version: 1.0.0-SNAPSHOT
 */
@Log4j2
public class HttpServer {

    private static final boolean SSL = System.getProperty("ssl") != null;

    private final String name;
    private final ApplicationContext context;

    public HttpServer(String name, ApplicationContext context) {
        this.name = name;
        this.context = context;
    }

    public void run() throws Exception {

        if (null != context) {
            new SpringUtil().setApplicationContext(context);
        }

        final SslContext ssl;
        final int[] server = ArthasUtil.parseServerParameter(name);
        final int count = server[0];
        final int port = server[1];

        if (SSL) {
            SelfSignedCertificate ssc = new SelfSignedCertificate();
            ssl = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();
        } else {
            ssl = null;
        }

        // initialize arthas context
        //
        new ArthasProcessHandler().init(name);

        // initialize arthas netty service
        //
        EventLoopGroup bossGroup = new NioEventLoopGroup(count);
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.option(ChannelOption.SO_BACKLOG, 1024)
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    .childOption(ChannelOption.TCP_NODELAY, true);
            bootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ArthasChannelInitializer(ssl));

            Channel ch = bootstrap.bind(port).sync().channel();
            log.info("HTTP Server: {}://{}:{}\n\n", SSL ? "https" : "http", InetAddress.getLocalHost().getHostAddress(), port);
            ch.closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}
